/*
 * Authors: Kyler Connare & Tony Hoang
 * Purpose: A turn-based 3D space shooter game with boundaries and Newtonian physics
 * Caveats: No major restrictions or known errors, though not all features are present compared to the rubric 
 *	(notably, lack of ship turning and the laser)
 */
#include "Main.h"


Main::Main(void)
{
}

Main::~Main(void)
{
}

// Game variables
Camera cam;
// Used to check which phase we're currently on
enum Phase { P1TURN, P2TURN, RESOLVE };
Phase curPhase = P1TURN;
bool gameOver = false;
bool player1Lost = false;
bool lostOOB = false;
int difficulty;

// Ship variables
// GluClylinder needs a pointer to this... thing
GLUquadric* cylinder1;
GLUquadric* cylinder2;
vec3 ship1Pos(10, 10, 10);
vec3 ship2Pos(290, 290, 290);
vec3 ship2Vel(0, 0, 0);
vec3 ship1Vel(0, 0, 0);
// Used during P1 & P2 TURNs to hold the velocity the player is adding this phase
vec3 shipVelocityToApply(0, 0, 0);
// Hold p1's VelocityToApply during p2 input phase
vec3 tempVelocity(0, 0, 0);

// Heads-up display variables and text
const float VIEW_SIZE = 300;
char player1String[25] = {"Player 1's Health: "};
char player2String[25] = {"Player 2's Health: "};
char player1Phase[25] = {"Player 1's Turn"};
char player2Phase[25] = {"Player 2's Turn"};
char resolveString[25] = {"Resolve"};
char player1Loses[25] = {"Player 1 loses!"};
char player2Loses[25] = {"Player 2 loses!"};
char outOfBounds[25] = {"Out of Bounds!"};
char destroyByLaser[25] = {"Killed by laser!"};
int player1HP = 10;
int player2HP = 10;

void init()
{
	cout << "Init() called" << endl;
	glClearColor (1.0, 1.0, 1.0, 0.0);
	glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, 300, 0, 300, 0, 300);
	cylinder1 = gluNewQuadric();
	cylinder2 = gluNewQuadric();
}

void display()
{
	//cout << "Camera position: " << cam.GetPosition() << " | Camera pitch: " << cam.GetPitchAmt() << " | Camera yaw: " << cam.GetYawAmt() << endl;
	
	// Set the camera up
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(50.0, 1.0, 0.0, 300.0);
	glMatrixMode(GL_MODELVIEW);          
	glLoadIdentity();

	//NECESSARY ROTATE for ease of use
	glRotatef(-90.0, 0, 1, 0);

	//camera controls
	glRotatef( cam.GetPitchAmt(), 0, 1, 0);
	glRotatef( cam.GetYawAmt(), 0, 0, 1);
	gluLookAt(cam.GetPosition().x, cam.GetPosition().y, cam.GetPosition().z + 150, cam.GetPosition().x, cam.GetPosition().y, cam.GetPosition().z, 0, 1, 0);

	glClear(GL_COLOR_BUFFER_BIT);

	/*
	// DEBUGGING LINES OF AWESOME
	glLineWidth(5); 
	glColor3f(0.0, 0.0, 0.0);
	glBegin(GL_LINES);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(300.0, 0.0, 0.0);
	glEnd();
	glBegin(GL_LINES);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(0.0, 300.0, 0.0);
	glEnd();
	glBegin(GL_LINES);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(0.0, 0.0, 300.0);
	glEnd();
	*/

	// CUBE THAT IS OUR SPACE
	glPushMatrix();
	glTranslatef(150, 150, 150);
	glEnable(GL_BLEND);
	glColor4f(0.0, 0.0, 0.0, 0.2);
	glutSolidCube(300);
	glDisable(GL_BLEND);
	glPopMatrix();

	
	//PLAYER 1's SHIP AND FIRING ARC
	glColor3f(1.0, 0.0, 0.0);
	glPushMatrix();
	glTranslatef(ship1Pos.x, ship1Pos.y, ship1Pos.z);
	gluCylinder( cylinder1, 5, 0, 0, 20, 20);
	gluCylinder( cylinder1, 5, 5, 16, 20, 20);
	glTranslatef(0, 0, 16);
	gluCylinder( cylinder1, 7, 0, 12, 20, 20);
	glEnable(GL_BLEND);
	glColor4f(1.0, 0.0, 0.0, 0.2);
	glTranslatef(0, 0, 12);
	gluCylinder( cylinder1, 0, difficulty * 15, 30, 20, 20);
	glDisable(GL_BLEND);
	glPopMatrix();

	//PLAYER 2'S SHIP AND FIRING ARC
	glColor3f(0.0, 1.0, 0.0);
	glPushMatrix();
	glTranslatef(ship2Pos.x, ship2Pos.y, ship2Pos.z);
	glRotatef(180, 0, 1, 0);
	gluCylinder( cylinder2, 5, 0, 0, 20, 20);
	gluCylinder( cylinder2, 5, 5, 16, 20, 20);
	glTranslatef(0, 0, 16);
	gluCylinder( cylinder2, 7, 0, 12, 20, 20);
	glEnable(GL_BLEND);
	glColor4f(0.0, 1.0, 0.0, 0.2);
	glTranslatef(0, 0, 12);
	gluCylinder( cylinder2, 0, difficulty * 15, 30, 20, 20);
	glDisable(GL_BLEND);
	glPopMatrix();


	//Heads-up Display - It's Magic!
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	glOrtho(0.0, VIEW_SIZE, 0.0, VIEW_SIZE, 0.0, VIEW_SIZE);
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
	glLoadIdentity();

	//PLAYER 1'S HP DISPLAY text
	glColor3f(0.0, 0.0, 0.0);
	glRasterPos3f(5, 280, 0);
	for (char* c = player1String; *c != '\0'; c++)
	{
		glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *c);
	}

	//PLAYER 1'S HP number
	char buffer[10];
	_itoa_s(player1HP, buffer, 10);
	glRasterPos3f(75,280, 0);
	for (char* c = buffer; *c != '\0'; c++)
	{
		glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *c);
	}

	//PLAYER 2'S HP DISPLAY text
	glColor3f(0.0, 0.0, 0.0);
	glRasterPos3f(210, 280, 0);
	for (char* c = player2String; *c != '\0'; c++)
	{
		glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *c);
	}

	//PLAYER 2'S HP number
	_itoa_s(player2HP, buffer, 10);
	glRasterPos3f(280,280, 0);
	for (char* c = buffer; *c != '\0'; c++)
	{
		glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *c);
	}

	//DISPLAY WHICH PHASE IT IS
	glRasterPos3f(0,0, 0);
	if (curPhase == P1TURN)
	{
		for (char* c = player1Phase; *c != '\0'; c++)
		{
			glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *c);
		}
	}
	else if (curPhase == P2TURN)
	{
		for (char* c = player2Phase; *c != '\0'; c++)
		{
			glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *c);
		}
	}
	else if (curPhase == RESOLVE)
	{
		for (char* c = resolveString; *c != '\0'; c++)
		{
			glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *c);
		}
	}

	//GAME OVER RESOLUTION MESSAGE
	if(gameOver)
	{
		//player 1 lost
		if (player1Lost)
		{
			glRasterPos3f(120, VIEW_SIZE/2, 0);
			for (char* c = player1Loses; *c != '\0'; c++)
			{
				glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *c);
			}
		}
		//Player 2 lost
		else
		{
			glRasterPos3f(120, VIEW_SIZE/2, 0);
			for (char* c = player2Loses; *c != '\0'; c++)
			{
				glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *c);
			}
		}

		//OUT OF BOUNDS
		if (lostOOB)
		{
			glRasterPos3f(120, VIEW_SIZE/2-25, 0);
			for (char* c = outOfBounds; *c != '\0'; c++)
			{
				glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *c);
			}
		}
		//LASER DEATH OF MISERY
		else
		{
			glRasterPos3f(120, VIEW_SIZE/2-25, 0);
			for (char* c = destroyByLaser; *c != '\0'; c++)
			{
				glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *c);
			}
		}
	}

	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);

	glFlush();
}

void keyboard( unsigned char key, int x, int y)
{
	switch( key ) {
	case 033: // Escape Key for exiting game
	    exit( EXIT_SUCCESS );
	    break;
	case 'i': // Move camera up
		cam.ChangePosition(0, 2, 0); break;
	case 'k': // Move camera down
		cam.ChangePosition(0, -2, 0); break;
	case 'j': // zoom camera in
		cam.ChangePosition(-2, 0, 0); break;
	case 'l': // zoom camera out
		cam.ChangePosition(2, 0, 0); break;
	case 'u': // move camera left
		cam.ChangePosition(0, 0, 2); break;
	case 'o': // move camera right
		cam.ChangePosition(0, 0, -2); break;
	case '7': // Pitch camera left 
		cam.ChangePitchAmt(-1); break;
	case '8': // Pitch camera right 
		cam.ChangePitchAmt(1); break;
	case '9': // yaw camera up 
		cam.ChangeYawAmt(-1); break;
	case '0': // yaw camera down 
		cam.ChangeYawAmt(1); break;

	case ' ': // Switch phases
		if(curPhase == P1TURN) {
			// Player one finished their turn
			tempVelocity = shipVelocityToApply;
			shipVelocityToApply = vec3(0, 0, 0);
			curPhase = P2TURN;
		} else if(curPhase == P2TURN) {
			// Player 2 finished their turn, resolve stuff
			ship1Vel += tempVelocity;
			ship2Vel += shipVelocityToApply;
			// Animate ships smoothly
			for(int i = 0; i < 140; i++) {
				ship1Pos += ship1Vel / 140;
				display();
			}
			for(int i = 0; i < 140; i++) {
				ship2Pos += ship2Vel / 140;
				display();
			}
			//cout << ship1Pos << endl;
			curPhase = RESOLVE;

			// Check win/loss
			if(ship1Pos.x < 0 || ship1Pos.y < 0 || ship1Pos.z < 0 || ship1Pos.x > 300 || ship1Pos.y > 300 || ship1Pos.z > 300 || player1HP <= 0) {
				// Player 1 loses!
				gameOver = true;
				player1Lost = true;
				if (player1HP > 0)
				{
					lostOOB = true;
				}
				cout << "Player 1 loses" << endl;
			} else if (ship2Pos.x < 0 || ship2Pos.y < 0 || ship2Pos.z < 0 || ship2Pos.x > 300 || ship2Pos.y > 300 || ship2Pos.z > 300 || player2HP <= 0) {
				// Player 2 loses!
				gameOver = true;
				player1Lost = false;
				if (player2HP > 0)
				{
					lostOOB = true;
				}
				cout << "Player 2 loses" << endl;
			}
			
		} else if(curPhase == RESOLVE && !gameOver) {
			// end the resolve phase
			shipVelocityToApply = vec3(0, 0, 0);
			curPhase = P1TURN;
		}
		break;

	// Ship controls
	case 'a': // Fire back engine once
		if(curPhase == P1TURN) {
			shipVelocityToApply.z += 1;
			if(shipVelocityToApply.z > 5)
				shipVelocityToApply.z = 5;
		} else if(curPhase == P2TURN) {
			shipVelocityToApply.z -= 1;
			if(shipVelocityToApply.z < -5)
				shipVelocityToApply.z = -5;
		}
		break;
	case 'q': // Fire front engine once
		if(curPhase == P1TURN) {
			shipVelocityToApply.z -= 1;
			if(shipVelocityToApply.z < -5)
				shipVelocityToApply.z = -5;
		} else if(curPhase == P2TURN) {
			shipVelocityToApply.z += 1;
			if(shipVelocityToApply.z > 5)
				shipVelocityToApply.z = 5;
		}
		break;
	case 's': // Fire top engine once
		if(curPhase == P1TURN) {
			shipVelocityToApply.y += 1;
			if(shipVelocityToApply.y > 5)
				shipVelocityToApply.y = 5;
		} else if(curPhase == P2TURN) {
			shipVelocityToApply.y += 1;
			if(shipVelocityToApply.y > 5)
				shipVelocityToApply.y = 5;
		}
		break;
	case 'w': // Fire bottom engine once
		if(curPhase == P1TURN) {
			shipVelocityToApply.y -= 1;
			if(shipVelocityToApply.y < -5)
				shipVelocityToApply.y = -5;
		} else if(curPhase == P2TURN) {
			shipVelocityToApply.y -= 1;
			if(shipVelocityToApply.y < -5)
				shipVelocityToApply.y = -5;
		}
		break;
	case 'd': // Fire left engine once
		if(curPhase == P1TURN) {
			shipVelocityToApply.x += 1;
			if(shipVelocityToApply.x > 5)
				shipVelocityToApply.x = 5;
		} else if(curPhase == P2TURN) {
			shipVelocityToApply.x -= 1;
			if(shipVelocityToApply.x < -5)
				shipVelocityToApply.x = -5;
		}
		break;
	case 'e': // Fire right engine once
		if(curPhase == P1TURN) {
			shipVelocityToApply.x -= 1;
			if(shipVelocityToApply.x < -5)
				shipVelocityToApply.x = -5;
		} else if(curPhase == P2TURN) {
			shipVelocityToApply.x += 1;
			if(shipVelocityToApply.x > 5)
				shipVelocityToApply.x = 5;
		}
		break;

	case '1': // Easy Difficulty 
		difficulty = 3; break;
	case '2': // Medium Difficulty
		difficulty = 2; break;
	case '3': // Hard Difficulty 
		difficulty = 1; break;
	}

}

int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode (GLUT_SINGLE | GLUT_RGBA);
	
    glutInitWindowSize(800, 600); 
    glutInitWindowPosition (100, 100);
	glutCreateWindow ("SPAAAAAAAAAAAAAAACE NOOOOOOOOOOOOOODLES");
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	init ();
    glutDisplayFunc(display); 
	glutKeyboardFunc( keyboard );
	glutIdleFunc( display );
    glutMainLoop();
    return 0; 
}
